home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 16 / CU Amiga Magazine's Super CD-ROM 16 (1997-10-16)(EMAP Images)(GB)[!][issue 1997-11].iso / CUCD / Graphics / Ghostscript / source / zupath.c < prev    next >
C/C++ Source or Header  |  1997-07-13  |  14KB  |  561 lines

  1. /* Copyright (C) 1990, 1996, 1997 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19. /* zupath.c */
  20. /* Operators related to user paths */
  21. #include "ghost.h"
  22. #include "errors.h"
  23. #include "oper.h"
  24. #include "idict.h"
  25. #include "dstack.h"
  26. #include "igstate.h"
  27. #include "iutil.h"
  28. #include "store.h"
  29. #include "stream.h"
  30. #include "ibnum.h"
  31. #include "gsmatrix.h"
  32. #include "gsstate.h"
  33. #include "gscoord.h"
  34. #include "gspaint.h"
  35. #include "gxfixed.h"
  36. #include "gxdevice.h"
  37. #include "gspath.h"
  38. #include "gzpath.h"        /* for saving path */
  39. #include "gzstate.h"        /* for accessing path */
  40.  
  41. /* Forward references */
  42. private int upath_append(P2(os_ptr, os_ptr));
  43. private int upath_stroke(P1(os_ptr));
  44.  
  45. /* ------ Insideness testing ------ */
  46.  
  47. /* Forward references */
  48. private int in_test(P2(os_ptr, int (*)(P1(gs_state *))));
  49. private int in_path(P3(os_ptr, os_ptr, gx_device *));
  50. private int in_path_result(P3(os_ptr, int, int));
  51. private int in_utest(P2(os_ptr, int (*)(P1(gs_state *))));
  52. private int in_upath(P2(os_ptr, gx_device *));
  53. private int in_upath_result(P3(os_ptr, int, int));
  54.  
  55. /* We use invalidexit, which the painting procedures cannot generate, */
  56. /* as an "error" to indicate that the hit detection device found a hit. */
  57. #define e_hit e_invalidexit
  58.  
  59. /* <x> <y> ineofill <bool> */
  60. /* <userpath> ineofill <bool> */
  61. private int
  62. zineofill(os_ptr op)
  63. {    return in_test(op, gs_eofill);
  64. }
  65.  
  66. /* <x> <y> infill <bool> */
  67. /* <userpath> infill <bool> */
  68. private int
  69. zinfill(os_ptr op)
  70. {    return in_test(op, gs_fill);
  71. }
  72.  
  73. /* <x> <y> instroke <bool> */
  74. /* <userpath> instroke <bool> */
  75. private int
  76. zinstroke(os_ptr op)
  77. {    return in_test(op, gs_stroke);
  78. }
  79.  
  80. /* <x> <y> <userpath> inueofill <bool> */
  81. /* <userpath1> <userpath2> inueofill <bool> */
  82. private int
  83. zinueofill(os_ptr op)
  84. {    return in_utest(op, gs_eofill);
  85. }
  86.  
  87. /* <x> <y> <userpath> inufill <bool> */
  88. /* <userpath1> <userpath2> inufill <bool> */
  89. private int
  90. zinufill(os_ptr op)
  91. {    return in_utest(op, gs_fill);
  92. }
  93.  
  94. /* <x> <y> <userpath> inustroke <bool> */
  95. /* <userpath1> <userpath2> inustroke <bool> */
  96. private int
  97. zinustroke(os_ptr op)
  98. {    /* This is different because of the optional matrix operand. */
  99.     int code = gs_gsave(igs);
  100.     int spop, npop;
  101.     gx_device hdev;
  102.  
  103.     if ( code < 0 )
  104.       return code;
  105.     if ( (spop = upath_stroke(op)) < 0 )
  106.        {    gs_grestore(igs);
  107.         return spop;
  108.        }
  109.     if ( (npop = in_path(op - spop, op, &hdev)) < 0 )
  110.        {    gs_grestore(igs);
  111.         return npop;
  112.        }
  113.     code = gs_stroke(igs);
  114.     return in_upath_result(op, npop + spop, code);
  115. }
  116.  
  117. /* ------ Internal routines ------ */
  118.  
  119. /* Define a minimal device for insideness testing. */
  120. /* It returns e_hit whenever it is asked to actually paint any pixels. */
  121. private dev_proc_fill_rectangle(hit_fill_rectangle);
  122. private gx_device hit_device =
  123. {    std_device_std_body(gx_device, 0, "hit detector",
  124.       0, 0, 1, 1),
  125.     {    NULL,            /* open_device */
  126.         NULL,            /* get_initial_matrix */
  127.         NULL,            /* sync_output */
  128.         NULL,            /* output_page */
  129.         NULL,            /* close_device */
  130.         gx_default_map_rgb_color,
  131.         gx_default_map_color_rgb,
  132.         hit_fill_rectangle,
  133.         NULL,            /* tile_rectangle */
  134.         NULL,            /* copy_mono */
  135.         NULL,            /* copy_color */
  136.         gx_default_draw_line,
  137.         NULL,            /* get_bits */
  138.         NULL,            /* get_params */
  139.         NULL,            /* put_params */
  140.         gx_default_map_cmyk_color,
  141.         NULL,            /* get_xfont_procs */
  142.         NULL,            /* get_xfont_device */
  143.         gx_default_map_rgb_alpha_color,
  144.         gx_default_get_page_device,
  145.         gx_default_get_alpha_bits,
  146.         NULL,            /* copy_alpha */
  147.         gx_default_get_band,
  148.         NULL,            /* copy_rop */
  149.         gx_default_fill_path,
  150.         NULL,        /* stroke_path */
  151.         NULL,        /* fill_mask */
  152.         gx_default_fill_trapezoid,
  153.         gx_default_fill_parallelogram,
  154.         gx_default_fill_triangle,
  155.         gx_default_draw_thin_line,
  156.         gx_default_begin_image,
  157.         gx_default_image_data,
  158.         gx_default_end_image,
  159.         gx_default_strip_tile_rectangle,
  160.         gx_default_strip_copy_rop,
  161.         gx_get_largest_clipping_box
  162.     }
  163. };
  164. /* Test for a hit when filling a rectangle. */
  165. private int
  166. hit_fill_rectangle(gx_device *dev, int x, int y, int w, int h,
  167.   gx_color_index color)
  168. {    fit_fill(dev, x, y, w, h); /* returns 0 if empty rectangle */
  169.     return e_hit;
  170. }
  171.  
  172. /* Do the work of the non-user-path insideness operators. */
  173. private int
  174. in_test(os_ptr op, int (*paintproc)(P1(gs_state *)))
  175. {    gx_device hdev;
  176.     int npop = in_path(op, op, &hdev);
  177.     int code;
  178.  
  179.     if ( npop < 0 ) return npop;
  180.     code = (*paintproc)(igs);
  181.     return in_path_result(op, npop, code);
  182. }
  183.  
  184. /* Set up a clipping path and device for insideness testing. */
  185. private int
  186. in_path(os_ptr oppath, os_ptr op, gx_device *phdev)
  187. {    int code = gs_gsave(igs);
  188.     int npop;
  189.     double uxy[2];
  190.  
  191.     if ( code < 0 )
  192.       return code;
  193.     code = num_params(oppath, 2, uxy);
  194.     if ( code >= 0 )
  195.        {    /* Aperture is a single pixel. */
  196.         gs_point dxy;
  197.         gs_fixed_rect fr;
  198.  
  199.         gs_transform(igs, uxy[0], uxy[1], &dxy);
  200.         fr.p.x = fixed_floor(float2fixed(dxy.x));
  201.         fr.p.y = fixed_floor(float2fixed(dxy.y));
  202.         fr.q.x = fr.p.x + fixed_1;
  203.         fr.q.y = fr.p.y + fixed_1;
  204.         code = gx_clip_to_rectangle(igs, &fr);
  205.         npop = 2;
  206.        }
  207.     else
  208.        {    /* Aperture is a user path. */
  209.         /* We have to set the clipping path without disturbing */
  210.         /* the current path. */
  211.         gx_path save;
  212.         save = *igs->path;
  213.         gx_path_reset(igs->path);    /* prevent newpath from */
  214.                         /* releasing path */
  215.         code = upath_append(oppath, op);
  216.         if ( code >= 0 )
  217.           code = gx_clip_to_path(igs);
  218.         gs_newpath(igs);        /* release upath */
  219.         *igs->path = save;
  220.         npop = 1;
  221.        }
  222.     if ( code < 0 )
  223.        {    gs_grestore(igs);
  224.         return code;
  225.        }
  226.     /* Install the hit detection device. */
  227.     gx_set_device_color_1(igs);
  228.     *phdev = hit_device;
  229.     phdev->width = phdev->height = max_int;
  230.     gx_device_fill_in_procs(phdev);
  231.     gx_set_device_only(igs, phdev);
  232.     return npop;
  233. }
  234.  
  235. /* Finish an insideness test. */
  236. private int
  237. in_path_result(os_ptr op, int npop, int code)
  238. {    int result;
  239.     gs_grestore(igs);        /* matches gsave in in_path */
  240.     switch ( code )
  241.        {
  242.     case e_hit:            /* found a hit */
  243.         result = 1;
  244.         break;
  245.     case 0:                /* completed painting without a hit */
  246.         result = 0;
  247.         break;
  248.     default:            /* error */
  249.         return code;
  250.        }
  251.     npop--;
  252.     pop(npop); op -= npop;
  253.     make_bool(op, result);
  254.     return 0;
  255.         
  256. }
  257.  
  258. /* Do the work of the user-path insideness operators. */
  259. private int
  260. in_utest(os_ptr op, int (*paintproc)(P1(gs_state *)))
  261. {    gx_device hdev;
  262.     int npop = in_upath(op, &hdev);
  263.     int code;
  264.  
  265.     if ( npop < 0 ) return npop;
  266.     code = (*paintproc)(igs);
  267.     return in_upath_result(op, npop, code);
  268. }
  269.  
  270. /* Set up a clipping path and device for insideness testing */
  271. /* with a user path. */
  272. private int
  273. in_upath(os_ptr op, gx_device *phdev)
  274. {    int code = gs_gsave(igs);
  275.     int npop;
  276.  
  277.     if ( code < 0 ) return code;
  278.     if ( (code = upath_append(op, op)) < 0 ||
  279.          (npop = in_path(op - 1, op, phdev)) < 0
  280.        )
  281.        {    gs_grestore(igs);
  282.         return code;
  283.        }
  284.     return npop + 1;
  285. }
  286.  
  287. /* Finish an insideness test with a user path. */
  288. private int
  289. in_upath_result(os_ptr op, int npop, int code)
  290. {    gs_grestore(igs);    /* matches gsave in in_upath */
  291.     return in_path_result(op, npop, code);
  292. }
  293.  
  294. /* ------ User paths ------ */
  295.  
  296. /* User path operator codes */
  297. typedef enum {
  298.   upath_setbbox = 0,
  299.   upath_moveto = 1,
  300.   upath_rmoveto = 2,
  301.   upath_lineto = 3,
  302.   upath_rlineto = 4,
  303.   upath_curveto = 5,
  304.   upath_rcurveto = 6,
  305.   upath_arc = 7,
  306.   upath_arcn = 8,
  307.   upath_arct = 9,
  308.   upath_closepath = 10,
  309.   upath_ucache = 11
  310. } upath_op;
  311. #define upath_op_max 11
  312. #define upath_repeat 32
  313. static byte up_nargs[upath_op_max + 1] =
  314.    { 4, 2, 2, 2, 2, 6, 6, 5, 5, 5, 0, 0 };
  315. /* Declare operator procedures not declared in opextern.h. */
  316. int zsetbbox(P1(os_ptr));
  317. int zarc(P1(os_ptr));
  318. int zarcn(P1(os_ptr));
  319. int zarct(P1(os_ptr));
  320. private int zucache(P1(os_ptr));
  321. #undef zp
  322. static op_proc_p up_ops[upath_op_max + 1] =
  323.    {    zsetbbox, zmoveto, zrmoveto, zlineto, zrlineto,
  324.     zcurveto, zrcurveto, zarc, zarcn, zarct,
  325.     zclosepath, zucache
  326.    };
  327.  
  328. /* - ucache - */
  329. private int
  330. zucache(os_ptr op)
  331. {    /* A no-op for now. */
  332.     return 0;
  333. }
  334.  
  335. /* <userpath> uappend - */
  336. private int
  337. zuappend(register os_ptr op)
  338. {    int code = gs_gsave(igs);
  339.     if ( code < 0 ) return code;
  340.     if ( (code = upath_append(op, op)) >= 0 )
  341.         code = gs_upmergepath(igs);
  342.     gs_grestore(igs);
  343.     if ( code < 0 ) return code;
  344.     pop(1);
  345.     return 0;
  346. }
  347.  
  348. /* <userpath> ueofill - */
  349. private int
  350. zueofill(register os_ptr op)
  351. {    int code = gs_gsave(igs);
  352.     if ( code < 0 ) return code;
  353.     if ( (code = upath_append(op, op)) >= 0 )
  354.         code = gs_eofill(igs);
  355.     gs_grestore(igs);
  356.     if ( code < 0 ) return code;
  357.     pop(1);
  358.     return 0;
  359. }
  360.  
  361. /* <userpath> ufill - */
  362. private int
  363. zufill(register os_ptr op)
  364. {    int code = gs_gsave(igs);
  365.     if ( code < 0 ) return code;
  366.     if ( (code = upath_append(op, op)) >= 0 )
  367.         code = gs_fill(igs);
  368.     gs_grestore(igs);
  369.     if ( code < 0 ) return code;
  370.     pop(1);
  371.     return 0;
  372. }
  373.  
  374. /* <userpath> ustroke - */
  375. /* <userpath> <matrix> ustroke - */
  376. private int
  377. zustroke(register os_ptr op)
  378. {    int code = gs_gsave(igs);
  379.     int npop;
  380.     if ( code < 0 ) return code;
  381.     if ( (code = npop = upath_stroke(op)) >= 0 )
  382.         code = gs_stroke(igs);
  383.     gs_grestore(igs);
  384.     if ( code < 0 ) return code;
  385.     pop(npop);
  386.     return 0;
  387. }
  388.  
  389. /* <userpath> ustrokepath - */
  390. /* <userpath> <matrix> ustrokepath - */
  391. private int
  392. zustrokepath(register os_ptr op)
  393. {    gx_path save;
  394.     int code, npop;
  395.  
  396.     /* Save and reset the path. */
  397.     save = *igs->path;
  398.     gx_path_reset(igs->path);
  399.     if ( (code = npop = upath_stroke(op)) < 0 ||
  400.          (code = gs_strokepath(igs)) < 0
  401.        )
  402.       { gs_newpath(igs);    /* release partial path */
  403.         *igs->path = save;
  404.         return code;
  405.       }
  406.     gx_path_release(&save);
  407.     pop(npop);
  408.     return 0;
  409. }
  410.  
  411. /* --- Internal routines --- */
  412.  
  413. /* Append a user path to the current path. */
  414. private int
  415. upath_append(os_ptr oppath, os_ptr op)
  416. {    check_read(*oppath);
  417.     gs_newpath(igs);
  418.     /****** ROUND tx AND ty ******/
  419.     if ( r_has_type(oppath, t_array) && r_size(oppath) == 2 &&
  420.          r_has_type(oppath->value.refs + 1, t_string)
  421.        )
  422.     {    /* 1st element is operators, 2nd is operands */
  423.         const ref *operands = oppath->value.refs;
  424.         int code, format;
  425.         int repcount = 1;
  426.         const byte *opp;
  427.         uint ocount, i = 0;
  428.  
  429.         code = num_array_format(operands);
  430.         if ( code < 0 )
  431.           return code;
  432.         format = code;
  433.         opp = oppath->value.refs[1].value.bytes;
  434.         ocount = r_size(&oppath->value.refs[1]);
  435.         while ( ocount-- )
  436.            {    byte opx = *opp++;
  437.             if ( opx > 32 )
  438.               repcount = opx - 32;
  439.             else if ( opx > upath_op_max )
  440.               return_error(e_rangecheck);
  441.             else        /* operator */
  442.                {    do
  443.                    {    byte opargs = up_nargs[opx];
  444.                     while ( opargs-- )
  445.                        {    push(1);
  446.                         code = num_array_get(operands, format, i++, op);
  447.                         switch ( code )
  448.                            {
  449.                         case t_integer:
  450.                             r_set_type_attrs(op, t_integer, 0);
  451.                             break;
  452.                         case t_real:
  453.                             r_set_type_attrs(op, t_real, 0);
  454.                             break;
  455.                         default:
  456.                             return_error(e_typecheck);
  457.                            }
  458.                        }
  459.                     code = (*up_ops[opx])(op);
  460.                     if ( code < 0 ) return code;
  461.                     op = osp;    /* resync */
  462.                    }
  463.                 while ( --repcount );
  464.                 repcount = 1;
  465.                }
  466.            }
  467.     }
  468.     else if ( r_is_array(oppath) )
  469.     {    /* Ordinary executable array. */
  470.         const ref *arp = oppath;
  471.         uint ocount = r_size(oppath);
  472.         long index = 0;
  473.         int argcount = 0;
  474.         int (*oproc)(P1(os_ptr));
  475.         int opx, code;
  476.  
  477.         for ( ; index < ocount; index++ )
  478.           { ref rup;
  479.             ref *defp;
  480.  
  481.             array_get(arp, index, &rup);
  482.             switch ( r_type(&rup) )
  483.               {
  484.               case t_integer:
  485.               case t_real:
  486.             argcount++;
  487.             push(1);
  488.             *op = rup;
  489.             break;
  490.               case t_name:
  491.             if ( !r_has_attr(&rup, a_executable) )
  492.                 return_error(e_typecheck);
  493.             if ( dict_find(systemdict, &rup, &defp) <= 0 )
  494.                 return_error(e_undefined);
  495.             if ( r_btype(defp) != t_operator )
  496.                 return_error(e_typecheck);
  497.             goto xop;
  498.               case t_operator:
  499.             defp = &rup;
  500. xop:            if ( !r_has_attr(defp, a_executable) )
  501.                 return_error(e_typecheck);
  502.             oproc = real_opproc(defp);
  503.             for ( opx = 0; opx <= upath_op_max; opx++ )
  504.                 if ( oproc == up_ops[opx] ) break;
  505.             if ( opx > upath_op_max || argcount != up_nargs[opx] )
  506.                 return_error(e_typecheck);
  507.             code = (*oproc)(op);
  508.             if ( code < 0 ) return code;
  509.             op = osp;    /* resync ostack pointer */
  510.             argcount = 0;
  511.             break;
  512.               default:
  513.             return_error(e_typecheck);
  514.               }
  515.            }
  516.         if ( argcount )
  517.             return_error(e_typecheck);    /* leftover args */
  518.     }
  519.     else
  520.       return_error(e_typecheck);
  521.     return 0;
  522. }
  523.  
  524. /* Append a user path to the current path, and then apply */
  525. /* a transformation if one is supplied. */
  526. private int
  527. upath_stroke(register os_ptr op)
  528. {    int code, npop;
  529.     gs_matrix mat;
  530.     if ( (code = read_matrix(op, &mat)) >= 0 )
  531.        {    if ( (code = upath_append(op - 1, op)) >= 0 )
  532.             code = gs_concat(igs, &mat);
  533.         npop = 2;
  534.        }
  535.     else
  536.        {    code = upath_append(op, op);
  537.         npop = 1;
  538.        }
  539.     return (code < 0 ? code : npop);
  540. }
  541.  
  542. /* ------ Initialization procedure ------ */
  543.  
  544. BEGIN_OP_DEFS(zupath_l2_op_defs) {
  545.         op_def_begin_level2(),
  546.         /* Insideness testing */
  547.     {"1ineofill", zineofill},
  548.     {"1infill", zinfill},
  549.     {"1instroke", zinstroke},
  550.     {"2inueofill", zinueofill},
  551.     {"2inufill", zinufill},
  552.     {"2inustroke", zinustroke},
  553.         /* User paths */
  554.     {"1uappend", zuappend},
  555.     {"1ueofill", zueofill},
  556.     {"1ufill", zufill},
  557.     {"1ustroke", zustroke},
  558.     {"1ustrokepath", zustrokepath},
  559.     {"0ucache", zucache},
  560. END_OP_DEFS(0) }
  561.